/**
 * Graph class implementaion
 *
 * \file graph.cpp
 * \author tomby
 * \date 04-01-2009
 **/

/*  Copyright (C) 2009  Tomas 'tomby' Bily

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
#include "dbc-sys.hpp"

using namespace dbc::peel;
using namespace dbc::temporal;

void
Graph::compute_sink_levels ()
{
  std::deque<Node::Ptr> queue;

  for (NodeIterator n = nodes->begin (); n != nodes->end (); n++)
    {
      if ((*n)->begin_out_edge (Edge::EMERGE) == (*n)->end_out_edge (Edge::EMERGE)
	  && (*n)->begin_in_edge (Edge::SINK) == (*n)->end_in_edge (Edge::SINK))
	{
	  queue.push_back (*n);
	  (*n)->set_sink_level (1);
	}
      else
	(*n)->set_sink_level (-1);
      /// Maybe use some stamp flags for cyclic graphs (or disable it)  
    }

  for (EdgeIterator e = edges->begin (); e != edges->end (); e++)
    (*e)->set_sink_level (-1);

  while (!queue.empty ())
    {
      Node::Ptr node = queue.front ();
      int max_sink = 1;
      
      queue.pop_front ();

      for (Node::edge_iter e = node->begin_in_edge (Edge::SINK); 
	   e != node->end_in_edge (Edge::SINK);
	   e++)
	max_sink = max_sink < (*e)->get_sink_level () ? (*e)->get_sink_level () : max_sink;
      
      node->set_sink_level (max_sink + 2);
      
      for (Node::edge_iter e = node->begin_out_edge (Edge::SINK); 
	   e != node->end_out_edge (Edge::SINK);
	   e++)
	{
	  (*e)->set_sink_level (max_sink + 3);
	  for (Edge::node_iter n = (*e)->begin_out_node (); 
	       n != (*e)->end_out_node ();
	       n++)
	    queue.push_back (*n);
	}

      for (Node::edge_iter e = node->begin_out_edge (Edge::EMERGE); 
	   e != node->end_out_edge (Edge::EMERGE);
	   e++)
	(*e)->set_sink_level (max_sink);

      for (Node::edge_iter e = node->begin_out_edge (Edge::INTERACT); 
	   e != node->end_out_edge (Edge::INTERACT);
	   e++)
	(*e)->set_sink_level (max_sink + 1);      
    }

  ///@todo: update non-connected edges (generic)
}

/** SimGraph implementation */

SimGraph::~SimGraph ()
{
  if (scheduler)
    delete scheduler;
}

void
SimGraph::start ()
{
  t = 0;

  if (!scheduler)
    scheduler = new SchedulerType ();

  scheduler->flush ();

  for (Graph::NodeIterator i = nodes->begin (); i != nodes->end (); ++i)
    { (*i)->get_stamp ().set_value (0); scheduler->add_event (*i); }
  
  for (Graph::EdgeIterator i = edges->begin (); i != edges->end (); ++i)
    { (*i)->get_stamp ().set_value (0); scheduler->add_event (*i); }

  scheduler->start ();
  compute_sink_levels ();
}

static bool cmp_nodes (const Node::Ptr& n1, const Node::Ptr&n2)
{ return n1->get_sink_level () > n2->get_sink_level (); }

static bool cmp_green_nodes (const Edge::Ptr& e1, const Edge::Ptr& e2)
{ return e1->get_sink_level () < e2->get_sink_level (); }

static bool cmp_red_edges (const Edge::Ptr& e1, const Edge::Ptr& e2)
{ return e1->get_sink_level () > e2->get_sink_level (); }

void
SimGraph::update_by_scheduler ()
{
  SchedulerType::CurrentEvenIter it = scheduler->current_event_iterator (); 
  std::vector<Node::Ptr> unodes;
  std::vector<Edge::Ptr> green_edges;
  std::vector<Edge::Ptr> red_edges;

  do 
    {
      if (IS_DBC_NODE (*it))
	unodes.push_back (DBC_NODE_PTR (*it));
      else if (IS_DBC_EDGE (*it))
	{
	  if (DBC_EDGE_PTR (*it)->get_etype () == Edge::SINK
	      || DBC_EDGE_PTR (*it)->get_color () == Edge::GREEN)
	    green_edges.push_back (DBC_EDGE_PTR (*it));
	  else
	    red_edges.push_back (DBC_EDGE_PTR (*it));
	}
    }
  while (it.next ());

  sort (unodes.begin (), unodes.end (), cmp_nodes);
  sort (green_edges.begin (), green_edges.end (), cmp_green_nodes);
  sort (red_edges.begin (), red_edges.end (), cmp_red_edges);

  for (std::vector<Edge::Ptr>::iterator i = green_edges.begin (); i != green_edges.end (); ++i)
    {
      (*i)->transform ();
      scheduler->next (static_cast<StampableEntity::Ptr>(*i));
    }

  for (std::vector<Node::Ptr>::iterator i = unodes.begin (); i != unodes.end (); ++i)
    {
      (*i)->update_state ();
      scheduler->next (static_cast<StampableEntity::Ptr>(*i));
    }

  for (std::vector<Edge::Ptr>::iterator i = red_edges.begin (); i != red_edges.end (); ++i)
    {
      (*i)->transform ();
      scheduler->next (static_cast<StampableEntity::Ptr>(*i));
    }
}

void
SimGraph::step ()
{
  update_by_scheduler ();
  t = (*(scheduler->current_event_iterator ()))->get_stamp ().get_value ();
  set_stamp (t);
}

void
SimGraph::update_state ()
{ step (); }
